<template>
 <VZPanel title="售后数据汇总表现" simple iconText="mdi-chart-box-multiple">
 <div style="margin:-16px">
 <v-card>
 <v-row class="px-3 pt-3">
 <v-col cols="2.5">
 <v-autocomplete
 v-model="selectedReferred"
 :items="referredItems"
 item-text="text"
 item-value="id"
 label="搜索零件简称"
 outlined
 dense
 clearable
 :filter="customFilter"
 :loading="loading"
 multiple
 ></v-autocomplete>
 </v-col>
 <v-col cols="2.5">
 <v-autocomplete
 v-model="selectedMaterial"
 :items="materialItems"
 item-text="text"
 item-value="id"
 label="搜索产品"
 outlined
 dense
 clearable
 :filter="customFilter"
 :loading="loading"
 ></v-autocomplete>
 </v-col>
 <v-col cols="2">
 <v-menu
 ref="startMenu"
 v-model="startMenu"
 :close-on-content-click="false"
 max-width="290"
 min-width="290"
 offset-y
 >
 <template v-slot:activator="{ on, attrs }">
 <v-text-field
 v-model="startYear"
 label="起始日期"
 readonly
 v-bind="attrs"
 v-on="on"
 outlined
 dense
 ></v-text-field>
 </template>
 <v-date-picker
 v-model="startYear"
 type="month"
 @change="handleStartDateChange"
 :max="endYear"
 :allowed-dates="allowedStartDates"
 locale="zh-cn"
 ></v-date-picker>
 </v-menu>
 </v-col>
 <v-col cols="2">
 <v-menu
 ref="endMenu"
 v-model="endMenu"
 :close-on-content-click="false"
 max-width="290"
 min-width="290"
 offset-y
 >
 <template v-slot:activator="{ on, attrs }">
 <v-text-field
 v-model="endYear"
 label="结束日期"
 readonly
 v-bind="attrs"
 v-on="on"
 outlined
 dense
 ></v-text-field>
 </template>
 <v-date-picker
 v-model="endYear"
 type="month"
 @change="handleEndDateChange"
 :min="startYear"
 :allowed-dates="allowedEndDates"
 locale="zh-cn"
 ></v-date-picker>
 </v-menu>
 </v-col>
 <v-col cols="auto" style="padding-top:13px;">
 <v-btn color="primary" @click="handleSearch" :loading="loading" :disabled="!canSearch" depressed>
 <v-icon left small>mdi-magnify</v-icon>查询
 </v-btn>
 </v-col>
 </v-row>
 <v-row class="px-3">
 <v-col cols="12">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>市场不良率</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="exportMarketRate">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <v-simple-table dense class="excel-style-table" v-if="marketRateData.years.length > 0">
 <thead>
 <tr style="border-bottom: 2px solid black;">
 <td rowspan="3" class="hdr">年份</td>
 <template v-for="(year, index) in marketRateData.years" :key="year">
 <th :colspan="getYearSpan(year)" :style="{ 'border-bottom': 'thin solid black','text-align':'center', borderRight: index < marketRateData.years.length - 1 ? '2px solid black' : '' }">
 {{ year }}
 </th>
 </template>
 </tr>
 </thead>
 <tbody>
 <tr style="border-bottom: 2px solid black;">
 <td class="hdr">月份</td>
 <template v-for="(year, yearIndex) in marketRateData.years" :key="yearIndex">
 <td v-for="month in getMonthsForYear(year)" :key="month">{{ month || '' }}</td>
 </template>
 </tr>
 <tr>
 <td class="hdr">故障数</td>
 <template v-for="year in marketRateData.years" :key="year">
 <td v-for="month in getMonthsForYear(year)" :key="month">{{ getMarketDataByYearMonth(year, month, 'fault') }}</td>
 </template>
 </tr>
 <tr>
 <td class="hdr">发货数</td>
 <template v-for="year in marketRateData.years" :key="year">
 <td v-for="month in getMonthsForYear(year)" :key="month">{{ getMarketDataByYearMonth(year, month, 'send') }}</td>
 </template>
 </tr>
 <tr>
 <td class="hdr">故障率</td>
 <template v-for="year in marketRateData.years" :key="year">
 <td v-for="month in getMonthsForYear(year)" :key="month">{{ getMarketDataByYearMonth(year, month, 'rate') }}</td>
 </template>
 </tr>
 <tr>
 <td class="hdr">平均率</td>
 <td width="290">{{ marketRateData.avgRate }}</td>
 </tr>
 </tbody>
 </v-simple-table>
 <div v-else class="text-center py-4 grey--text">暂无数据</div>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>售后索赔日期数量分布</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(1)" class="mr-2">
 <v-icon>{{ expandedTables[1] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportClaimChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportClaimExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="claimChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-data-table
 v-show="expandedTables[1]"
 :headers="claimHeaders"
 :items="claimTableData"
 :items-per-page="5"
 dense
 class="mt-2"
 hide-default-footer
 ></v-data-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>故障件生产批次分布</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(2)" class="mr-2">
 <v-icon>{{ expandedTables[2] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportBatchChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportBatchExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="batchChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-simple-table dense v-show="expandedTables[2]" class="mt-2">
 <thead>
 <tr>
 <th>生产日期</th>
 <th class="text-right">故障数量</th>
 <th class="text-right">发货数量</th>
 <th class="text-right">故障率(%)</th>
 </tr>
 </thead>
 <tbody>
 <tr v-for="item in batchTableData" :key="item.name">
 <td>{{ item.name }}</td>
 <td class="text-right">{{ item.num }}</td>
 <td class="text-right">{{ item.sendNum }}</td>
 <td class="text-right">{{ item.rate }}%</td>
 </tr>
 </tbody>
 </v-simple-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>故障里程分布(k公里)</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(3)" class="mr-2">
 <v-icon>{{ expandedTables[3] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportMileageChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportMileageExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="mileageChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-data-table
 v-show="expandedTables[3]"
 :headers="mileageHeaders"
 :items="mileageTableData"
 :items-per-page="5"
 dense
 class="mt-2"
 ></v-data-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>售后分析缺陷模式分布</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(4)" class="mr-2">
 <v-icon>{{ expandedTables[4] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportDefectChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportDefectExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="defectChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-data-table
 v-show="expandedTables[4]"
 :headers="defectHeaders"
 :items="defectTableData"
 :items-per-page="5"
 dense
 class="mt-2"
 ></v-data-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>售后经销商4S店分布</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(5)" class="mr-2">
 <v-icon>{{ expandedTables[5] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportStoreChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportStoreExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="storeChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-data-table
 v-show="expandedTables[5]"
 :headers="storeHeaders"
 :items="storeTableData"
 :items-per-page="5"
 dense
 class="mt-2"
 ></v-data-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 <v-col cols="6">
 <v-card outlined class="chart-card">
 <v-card-title class="chart-title">
 <span>售后用户描述缺陷模式分布</span>
 <v-spacer></v-spacer>
 <v-btn icon small @click="toggleTable(6)" class="mr-2">
 <v-icon>{{ expandedTables[6] ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
 </v-btn>
 <v-btn icon small @click="exportUserDefectChart" class="mr-1">
 <v-icon color="primary">mdi-image</v-icon>
 </v-btn>
 <v-btn icon small @click="exportUserDefectExcel">
 <v-icon color="success">mdi-file-excel</v-icon>
 </v-btn>
 </v-card-title>
 <v-divider></v-divider>
 <v-card-text>
 <div ref="userDefectChart" style="width:100%;height:320px"></div>
 <v-expand-transition>
 <v-data-table
 v-show="expandedTables[6]"
 :headers="userDefectHeaders"
 :items="userDefectTableData"
 :items-per-page="5"
 dense
 class="mt-2"
 ></v-data-table>
 </v-expand-transition>
 </v-card-text>
 </v-card>
 </v-col>
 </v-row>
 </v-card>
 </div>
 </VZPanel>
</template>
<script>
export default {
 data() {
 return {
 materialSql: "SELECT id,CONCAT(name,' ',ASSEMBLY_CODE) name FROM tb_standard_assembly_product WHERE MARK_FOR_DELETE = 0",
 referredSql: "SELECT c.id,CONCAT(c.SIMPLE_CALL,' ',c.CODE) name FROM tt_sale_record_qms a " +
 "LEFT JOIN tb_standard_assembly_product b ON a.MATERIAL_ID = b.ID " +
 "LEFT JOIN tb_standard_assembly_product_referred c ON c.id = b.REFERRED_ID " +
 "WHERE c.SIMPLE_CALL IS NOT NULL " +
 "GROUP BY c.ID ",
 materialItems: [],
 referredItems: [],
 selectedMaterial: '',
 selectedReferred: [],
 startYear: '',
 endYear: '',
 startMenu: false,
 endMenu: false,
 loading: true,
 materialIds: [],
 materialCodes: [],
 expandedTables: [false, false, false, false, false, false, false],
 chartInstances: {},
 marketRateData: {
 years: [],
 faultData: [],
 sendData: [],
 rateData: [],
 avgRate: '',
 nameList: [],
 },
 batchTableData: [],
 claimHeaders: [],
 claimTableData: [],
 claimYearData: {},
 defectHeaders: [
 { text: '缺陷模式', value: 'name', align: 'start' },
 { text: '数量', value: 'value', align: 'end' },
 { text: '占比(%)', value: 'percentage', align: 'end' },
 { text: '累计占比(%)', value: 'cumulative', align: 'end' }
 ],
 defectTableData: [],
 defectIds: [],
 mileageHeaders: [
 { text: '里程区间(km)', value: 'range' },
 { text: '数量', value: 'count', align: 'center' }
 ],
 mileageTableData: [],
 distanceRanges: [
 { min: 0, max: 1000, label: '0-1000' },
 { min: 1000, max: 5000, label: '1000-5000' },
 { min: 5000, max: 10000, label: '5000-10000' },
 { min: 10000, max: 50000, label: '10000-50000' },
 { min: 50000, max: 150000, label: '50000-150000' }
 ],
 storeHeaders: [
 { text: '4S店名称', value: 'name', align: 'start' },
 { text: '数量', value: 'value', align: 'end' },
 { text: '占比(%)', value: 'percentage', align: 'end' },
 { text: '累计占比(%)', value: 'cumulative', align: 'end' }
 ],
 storeTableData: [],
 userDefectHeaders: [
 { text: '缺陷模式', value: 'name', align: 'start' },
 { text: '数量', value: 'value', align: 'end' },
 { text: '占比(%)', value: 'percentage', align: 'end' },
 { text: '累计占比(%)', value: 'cumulative', align: 'end' }
 ],
 userDefectTableData: [],
 };
 },
 computed: {
 years() {
 if (!this.startYear || !this.endYear) return [];
 const start = parseInt(this.startYear.split('-')[0]);
 const end = parseInt(this.endYear.split('-')[0]);
 return Array.from({ length: end - start + 1 }, (_, i) => String(start + i));
 },
 canSearch() {
 return (this.selectedReferred.length > 0 || this.selectedMaterial) && this.startYear && this.endYear;
 }
 },
 methods: {
 toggleTable(index) {
 this.$set(this.expandedTables, index, !this.expandedTables[index]);
 },
 customFilter(item, queryText, itemText) {
 const textOne = itemText.toLowerCase();
 const searchText = queryText.toLowerCase();
 return textOne.indexOf(searchText) > -1;
 },
 initYearOptions() {
 const currentDate = new Date();
 const currentYear = currentDate.getFullYear();
 const currentMonth = String(currentDate.getMonth() + 1).padStart(2, '0');
 this.endYear = `${currentYear}-${currentMonth}`;
 const startDate = new Date(currentDate);
 startDate.setFullYear(currentYear - 2);
 this.startYear = `${startDate.getFullYear()}-${currentMonth}`;
 },
 getMonthDiff(date1, date2) {
 const d1 = new Date(date1);
 const d2 = new Date(date2);
 return (d2.getFullYear() - d1.getFullYear()) * 12 + (d2.getMonth() - d1.getMonth());
 },
 allowedStartDates(val) {
 if (!this.endYear) return true;
 const diff = this.getMonthDiff(val, this.endYear);
 return diff <= 24 && diff >= 0;
 },
 allowedEndDates(val) {
 if (!this.startYear) return true;
 const diff = this.getMonthDiff(this.startYear, val);
 return diff <= 24 && diff >= 0;
 },
 handleStartDateChange() {
 this.startMenu = false;
 if (this.startYear && this.endYear) {
 const diff = this.getMonthDiff(this.startYear, this.endYear);
 if (diff > 24) {
 const startDate = new Date(this.startYear);
 startDate.setMonth(startDate.getMonth() + 24);
 this.endYear = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`;
 }
 }
 },
 handleEndDateChange() {
 this.endMenu = false;
 if (this.startYear && this.endYear) {
 const diff = this.getMonthDiff(this.startYear, this.endYear);
 if (diff > 24) {
 const endDate = new Date(this.endYear);
 endDate.setMonth(endDate.getMonth() - 24);
 this.startYear = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}`;
 }
 }
 },
 async handleSearch() {
 if (!this.canSearch) return;
 this.loading = true;
 try {
 if (this.selectedReferred && this.selectedReferred.length > 0) {
 this.selectedMaterial = '';
 await this.loadMaterialIdsByReferred();
 await this.loadMaterialCodesByReferred();
 } else if (this.selectedMaterial) {
 await this.loadCodesByMaterialId();
 }
 if (this.materialIds.length > 0 && this.materialCodes.length > 0) {
 await this.loadAllData();
 }
 } finally {
 this.loading = false;
 }
 },
 loadReferredData() {
 let _this = this;
 _this.referredItems = [];
 let params = { "SQL": this.referredSql };
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: {
 functionParam: JSON.stringify(params)
 },
 callBack(res) {
 if (res.success && res.rows.length > 0) {
 let dataList = res.rows;
 let referredList = [];
 dataList.forEach((item) => {
 let map = {};
 map.text = item.name;
 map.id = item.id;
 referredList.push(map);
 })
 _this.referredItems = referredList;
 if (_this.referredItems.length > 0) {
 _this.selectedReferred = [_this.referredItems[0].id];
 _this.loadMaterialIdsByReferred().then(() => {
 _this.loadMaterialCodesByReferred().then(() => {
 if (_this.materialIds.length > 0) {
 _this.loadAllData();
 }
 });
 });
 }
 }
 },
 })
 },
 loadMaterialData() {
 let _this = this;
 _this.materialItems = [];
 let params = { "SQL": this.materialSql };
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: {
 functionParam: JSON.stringify(params)
 },
 callBack(res) {
 if (res.success && res.rows.length > 0) {
 let dataList = res.rows;
 let materialList = [];
 dataList.forEach((item) => {
 let map = {};
 map.text = item.name;
 map.id = item.id;
 materialList.push(map);
 })
 _this.materialItems = materialList;
 }
 _this.loading = false;
 },
 })
 },
 loadMaterialIdsByReferred() {
 if (!this.selectedReferred || this.selectedReferred.length === 0) {
 this.materialIds = this.selectedMaterial ? [this.selectedMaterial] : [];
 return Promise.resolve();
 }
 let _this = this;
 let params = {
 "SQL": `SELECT DISTINCT a.id
 FROM tb_standard_assembly_product a
 WHERE a.MARK_FOR_DELETE = 0
 AND a.REFERRED_ID IN (${this.selectedReferred.join(',')})`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: {
 functionParam: JSON.stringify(params)
 },
 callBack(res) {
 if (res.success && res.rows.length > 0) {
 _this.materialIds = res.rows.map(item => item.id);
 } else {
 _this.materialIds = [];
 }
 resolve();
 },
 });
 });
 },
 loadMaterialCodesByReferred() {
 if (!this.selectedReferred.length) {
 this.materialCodes = this.selectedMaterial ? [this.selectedMaterial] : [];
 return Promise.resolve();
 }
 let _this = this;
 let params = {
 "SQL": `SELECT DISTINCT a.ASSEMBLY_CODE as code FROM tb_standard_assembly_product a WHERE a.MARK_FOR_DELETE = 0 AND a.REFERRED_ID IN (${this.selectedReferred.join(',')})`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack(res) {
 _this.materialCodes = res.success && res.rows.length > 0 ? res.rows.map((item) => item.code) : [];
 resolve();
 },
 });
 });
 },
 loadCodesByMaterialId() {
 if (!this.selectedMaterial) {
 return Promise.resolve();
 }
 let params = {
 "SQL": `SELECT DISTINCT a.ASSEMBLY_CODE as code FROM tb_standard_assembly_product a WHERE a.MARK_FOR_DELETE = 0 AND a.ID = ${this.selectedMaterial}`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack: (res) => {
 this.materialCodes = res.success && res.rows.length > 0 ? res.rows.map((item) => item.code) : [];
 resolve();
 },
 });
 });
 },
 async loadAllData() {
 if (this.materialIds.length === 0) return;
 this.loading = true;
 await Promise.all([
 this.loadMarketRateData(),
 this.loadBatchData(),
 this.loadClaimData(),
 this.loadDefectData(),
 this.loadMileageData(),
 this.loadStoreData(),
 this.loadUserDefectData()
 ]);
 this.loading = false;
 },
 async loadMarketRateData() {
 if (!this.materialIds.length || !this.materialCodes.length) return;
 this.marketRateData = {
 years: [],
 faultData: [],
 sendData: [],
 rateData: [],
 avgRate: '',
 nameList: [],
 };
 const faultParams = {
 SQL: `SELECT DATE_FORMAT(MADE_TIME, '%Y-%m') AS name, COUNT(*) AS num 
 FROM tt_sale_record_qms a 
 WHERE a.MARK_FOR_DELETE = 0 
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND MADE_TIME IS NOT NULL
 AND DATE_FORMAT(MADE_TIME, '%Y-%m') != ''
 AND (DATE_FORMAT(MADE_TIME, '%Y-%m') >= '${this.startYear}' AND DATE_FORMAT(MADE_TIME, '%Y-%m') <= '${this.endYear}') 
 GROUP BY DATE_FORMAT(MADE_TIME, '%Y-%m') 
 ORDER BY DATE_FORMAT(MADE_TIME, '%Y-%m');`
 };
 const formattedCodes = this.materialCodes.map(code => `'${code}'`).join(',');
 const sendParams = {
 SQL: `SELECT CASE WHEN YEAR_MONTHS IS NULL THEN ' ' ELSE DATE_FORMAT(YEAR_MONTHS, '%Y-%m') END AS name, SUM(SHIPPED_QTY) AS sendNum 
 FROM TB_PART_SENDOUT a 
 WHERE a.MATNR in (${formattedCodes}) 
 AND (YEAR_MONTHS IS NULL OR (DATE_FORMAT(YEAR_MONTHS, '%Y-%m') >= '${this.startYear}' AND DATE_FORMAT(YEAR_MONTHS, '%Y-%m') <= '${this.endYear}')) 
 GROUP BY CASE WHEN YEAR_MONTHS IS NULL THEN '无日期' ELSE DATE_FORMAT(YEAR_MONTHS, '%Y-%m') END 
 ORDER BY CASE WHEN name = '无日期' THEN 1 ELSE 0 END, name;`
 };
 let _this = this;
 await new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(faultParams) },
 callBack: (res) => {
 if (res.success) {
 _this.marketRateData.nameList = res.rows.map(item => item.name);
 _this.marketRateData.faultData = res.rows.map(item => item.num);
 }
 resolve();
 },
 });
 });
 await new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(sendParams) },
 callBack: (res) => {
 if (res.success) {
 const sendNameList = res.rows.map(item => item.name.trim());
 const sendNumList = res.rows.map(item => item.sendnum);
 const faultNameList = _this.marketRateData.nameList;
 const faultNumList = _this.marketRateData.faultData;
 const faultMap = {};
 faultNameList.forEach((name, idx) => {
 faultMap[name] = faultNumList[idx];
 });
 _this.marketRateData.nameList = sendNameList;
 _this.marketRateData.sendData = sendNumList;
 _this.marketRateData.faultData = sendNameList.map(name => faultMap[name] || 0);
 _this.marketRateData.rateData = sendNameList.map((name, index) => {
 const fault = _this.marketRateData.faultData[index] || 0;
 const send = sendNumList[index] || 0;
 return (send > 0 && fault !== null && fault !== undefined) ? (fault / send * 100).toFixed(2) + '%' : '0%';
 });
 const totalFault = _this.marketRateData.faultData.reduce((a, b) => a + b, 0);
 const totalSend = _this.marketRateData.sendData.reduce((a, b) => a + b, 0);
 _this.marketRateData.avgRate = totalSend > 0 ? (totalFault / totalSend * 100).toFixed(2) + '%' : '0%';
 _this.marketRateData.years = _this.years;
 }
 resolve();
 },
 });
 });
 },
 async loadBatchData() {
 if (!this.materialIds.length || !this.materialCodes.length) return;
 let _this = this;
 const faultParams = {
 SQL: `SELECT DATE_FORMAT(MADE_TIME, '%Y-%m') AS name, COUNT(*) AS num 
 FROM tt_sale_record_qms a 
 WHERE a.MARK_FOR_DELETE = 0 
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND MADE_TIME IS NOT NULL
 AND DATE_FORMAT(MADE_TIME, '%Y-%m') != ''
 AND (DATE_FORMAT(MADE_TIME, '%Y-%m') >= '${this.startYear}' AND DATE_FORMAT(MADE_TIME, '%Y-%m') <= '${this.endYear}') 
 GROUP BY DATE_FORMAT(MADE_TIME, '%Y-%m') 
 ORDER BY DATE_FORMAT(MADE_TIME, '%Y-%m');`
 };
 const formattedCodes = this.materialCodes.map(code => `'${code}'`).join(',');
 const sendParams = {
 SQL: `SELECT CASE WHEN YEAR_MONTHS IS NULL THEN ' ' ELSE DATE_FORMAT(YEAR_MONTHS, '%Y-%m') END AS name, SUM(SHIPPED_QTY) AS sendNum 
 FROM TB_PART_SENDOUT a 
 WHERE a.MATNR in (${formattedCodes}) 
 AND (YEAR_MONTHS IS NULL OR (DATE_FORMAT(YEAR_MONTHS, '%Y-%m') >= '${this.startYear}' AND DATE_FORMAT(YEAR_MONTHS, '%Y-%m') <= '${this.endYear}')) 
 GROUP BY CASE WHEN YEAR_MONTHS IS NULL THEN '无日期' ELSE DATE_FORMAT(YEAR_MONTHS, '%Y-%m') END 
 ORDER BY CASE WHEN name = '无日期' THEN 1 ELSE 0 END, name;`
 };
 let nameList = [], faultDataList = [], sendDataList = [];
 await new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(faultParams) },
 callBack(res) {
 if (res.success) {
 nameList = res.rows.map(item => item.name);
 faultDataList = res.rows.map(item => item.num);
 }
 resolve();
 },
 });
 });
 await new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(sendParams) },
 callBack(res) {
 if (res.success) {
 sendDataList = res.rows.map(item => item.sendnum);
 }
 resolve();
 },
 });
 });
 _this.batchTableData = nameList.map((name, index) => {
 const faultNum = faultDataList[index] || 0;
 const sendNum = sendDataList[index] || 0;
 const rate = sendNum > 0 ? ((faultNum / sendNum) * 100).toFixed(2) : '0.00';
 return {
 name: name,
 num: faultNum,
 sendNum: sendNum,
 rate: rate
 };
 });
 _this.renderBatchChart();
 },
 loadClaimData() {
 if (this.materialIds.length === 0) return Promise.resolve();
 let _this = this;
 this.claimYearData = {};
 let allMonths = Array.from({length: 12}, (_, i) => String(i + 1).padStart(2, '0'));
 let promises = this.years.map(year => {
 let params = {
 "SQL": `SELECT DATE_FORMAT(back_month, '%m') AS name,COUNT(*) AS num
 FROM tt_sale_record_qms a
 WHERE a.MARK_FOR_DELETE = 0
 AND a.MATERIAL_ID IN (${this.materialIds.join(',')})
 AND DATE_FORMAT(back_month, '%Y') = '${year}'
 GROUP BY DATE_FORMAT(back_month, '%m')
 ORDER BY name;`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack(res) {
 if (res.success) {
 let monthMap = {};
 allMonths.forEach(month => {
 monthMap[month] = 0;
 });
 res.rows.forEach((item) => {
 monthMap[item.name] = item.num;
 });
 _this.claimYearData[year] = allMonths.map(month => monthMap[month]);
 }
 resolve();
 },
 });
 });
 });
 return Promise.all(promises).then(() => {
 _this.updateClaimTableData();
 _this.renderClaimChart();
 });
 },
 loadDefectData() {
 let _this = this;
 let params = {
 "SQL": `SELECT IFNULL(b.id, 0) id, IFNULL(b.FIRST_ANALYSE, '未定义') name, COUNT(1) num
 FROM tt_sale_record_qms a
 LEFT JOIN tt_mqp_defect b ON a.DEFECT_ID = b.id
 WHERE a.MARK_FOR_DELETE = 0
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND (DATE_FORMAT(a.MADE_TIME, '%Y-%m') >= '${this.startYear}'
 AND DATE_FORMAT(a.MADE_TIME, '%Y-%m') <= '${this.endYear}')
 GROUP BY b.id, b.FIRST_ANALYSE
 ORDER BY num DESC`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack(res) {
 if (res.success) {
 const nameList = res.rows.map(item => item.name || '未定义');
 const numList = res.rows.map(item => item.num);
 _this.defectIds = res.rows.map(item => item.id);
 const total = numList.reduce((a, b) => a + b, 0);
 let cumulativePercent = 0;
 _this.defectTableData = nameList.map((name, index) => {
 const percentage = (numList[index] / total * 100).toFixed(1);
 cumulativePercent += parseFloat(percentage);
 return {
 name: name,
 value: numList[index],
 percentage: percentage,
 cumulative: cumulativePercent.toFixed(1)
 };
 });
 _this.renderDefectChart(nameList, numList);
 }
 resolve();
 },
 });
 });
 },
 loadMileageData() {
 let _this = this;
 const queries = this.distanceRanges.map((range, index) => {
 const minCondition = index === 0 ? `>= ${range.min}` : `> ${range.min}`;
 const sql = `SELECT COUNT(*) as count
 FROM tt_sale_record_qms a
 WHERE a.MARK_FOR_DELETE = 0
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND DATE_FORMAT(MADE_TIME, '%Y-%m') >= '${this.startYear}'
 AND DATE_FORMAT(MADE_TIME, '%Y-%m') <= '${this.endYear}'
 AND DRIVE_DISTANCE ${minCondition}
 AND DRIVE_DISTANCE <= ${range.max}`;
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify({ "SQL": sql }) },
 callBack(res) {
 if (res.success) {
 resolve({
 range: range.label,
 count: res.rows[0].count || 0
 });
 } else {
 resolve({
 range: range.label,
 count: 0
 });
 }
 },
 });
 });
 });
 return Promise.all(queries).then(results => {
 _this.mileageTableData = results;
 _this.renderMileageChart(results);
 });
 },
 loadStoreData() {
 let _this = this;
 let params = {
 "SQL": `SELECT IFNULL(a.REPAIR_STATION_NAME, '未定义') name, COUNT(1) num
 FROM tt_sale_record_qms a
 WHERE a.MARK_FOR_DELETE = 0
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND (DATE_FORMAT(a.MADE_TIME, '%Y-%m') >= '${this.startYear}'
 AND DATE_FORMAT(a.MADE_TIME, '%Y-%m') <= '${this.endYear}')
 GROUP BY a.REPAIR_STATION_NAME
 ORDER BY num DESC`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack(res) {
 if (res.success) {
 const nameList = res.rows.map(item => item.name || '未定义');
 const numList = res.rows.map(item => item.num);
 const total = numList.reduce((a, b) => a + b, 0);
 let cumulativePercent = 0;
 _this.storeTableData = nameList.map((name, index) => {
 const percentage = (numList[index] / total * 100).toFixed(1);
 cumulativePercent += parseFloat(percentage);
 return {
 name: name,
 value: numList[index],
 percentage: percentage,
 cumulative: cumulativePercent.toFixed(1)
 };
 }).sort((a, b) => b.value - a.value);
 _this.renderStoreChart(nameList, numList);
 }
 resolve();
 },
 });
 });
 },
 loadUserDefectData() {
 let _this = this;
 let params = {
 "SQL": `SELECT IFNULL(a.CONSUMER_DESCRIBE_EXTRACT, '未定义') name, COUNT(1) num
 FROM tt_sale_record_qms a
 WHERE a.MARK_FOR_DELETE = 0
 AND a.MATERIAL_ID in (${this.materialIds.join(',')})
 AND (DATE_FORMAT(a.MADE_TIME, '%Y-%m') >= '${this.startYear}'
 AND DATE_FORMAT(a.MADE_TIME, '%Y-%m') <= '${this.endYear}')
 GROUP BY a.CONSUMER_DESCRIBE_EXTRACT
 ORDER BY num DESC`
 };
 return new Promise((resolve) => {
 app.utils.ajaxRequest({
 hideMsg: true,
 hideMask: true,
 url: app.path().dispose + "?functionCode=QMS_CUSTOM_QUERY_SQL",
 params: { functionParam: JSON.stringify(params) },
 callBack(res) {
 if (res.success) {
 const nameList = res.rows.map(item => item.name || '未定义');
 const numList = res.rows.map(item => item.num);
 const total = numList.reduce((a, b) => a + b, 0);
 let cumulativePercent = 0;
 _this.userDefectTableData = nameList.map((name, index) => {
 const percentage = (numList[index] / total * 100).toFixed(1);
 cumulativePercent += parseFloat(percentage);
 return {
 name: name,
 value: numList[index],
 percentage: percentage,
 cumulative: cumulativePercent.toFixed(1)
 };
 });
 _this.renderUserDefectChart(nameList, numList);
 }
 resolve();
 },
 });
 });
 },
 renderBatchChart() {
 if (!this.$refs.batchChart) return;
 if (!this.chartInstances.batch) {
 this.chartInstances.batch = this.$echarts.init(this.$refs.batchChart);
 }
 const nameList = this.batchTableData.map(item => item.name);
 const numList = this.batchTableData.map(item => item.num);
 const rateList = this.batchTableData.map(item => parseFloat(item.rate));
 let option = {
 tooltip: {
 trigger: 'axis',
 axisPointer: { type: 'shadow' },
 formatter: (params) => {
 let result = `${params[0].name}<br/>`;
 params.forEach(item => {
 if (item.seriesName === '故障数量') {
 result += `${item.seriesName}: ${item.value}个<br/>`;
 } else if (item.seriesName === '故障率') {
 result += `${item.seriesName}: ${item.value}%<br/>`;
 }
 });
 return result;
 }
 },
 legend: {
 data: ['故障数量', '故障率'],
 right: 'center',
 top: 0
 },
 grid: {
 left: '3%',
 right: '4%',
 bottom: '15%',
 containLabel: true
 },
 xAxis: {
 type: 'category',
 data: nameList,
 axisLabel: {
 interval: 0,
 rotate: 45,
 fontSize: 10
 }
 },
 yAxis: [
 {
 type: 'value',
 name: '数量',
 position: 'left'
 },
 {
 type: 'value',
 name: '故障率(%)',
 position: 'right',
 axisLabel: {
 formatter: '{value}%'
 }
 }
 ],
 series: [
 {
 name: '故障数量',
 data: numList,
 type: 'bar',
 yAxisIndex: 0,
 itemStyle: {
 color: '#5470c6',
 normal: {
 label: {
 show: true,
 position: 'top',
 fontSize: 10
 }
 }
 }
 },
 {
 name: '故障率',
 data: rateList,
 type: 'line',
 yAxisIndex: 1,
 smooth: false,
 itemStyle: {
 color: '#ee6666'
 },
 lineStyle: {
 width: 2
 },
 label: {
 show: true,
 position: 'top',
 fontSize: 10,
 formatter: '{c}%'
 }
 }
 ]
 };
 this.chartInstances.batch.setOption(option);
 let _this = this;
 this.chartInstances.batch.off('click');
 this.chartInstances.batch.on('click', function (params) {
 let dateRange = params.name;
 let startDate, endDate, made_time_null;
 if (dateRange === '无日期') {
 made_time_null = 1;
 } else {
 const [year, month] = dateRange.split('-');
 startDate = `${year}-${month}-01`;
 const lastDay = new Date(parseInt(year), parseInt(month), 0).getDate();
 endDate = `${year}-${month}-${lastDay}`;
 }
 _this.$emit('toPage', 'v4a964e28354dd5945af2da28ad', {
 material_id: _this.materialIds.join(','),
 btime: startDate,
 etime: endDate,
 made_time_null: made_time_null
 });
 });
 },
 renderClaimChart() {
 if (!this.$refs.claimChart) return;
 if (!this.chartInstances.claim) {
 this.chartInstances.claim = this.$echarts.init(this.$refs.claimChart);
 }
 let allMonths = Array.from({length: 12}, (_, i) => String(i + 1).padStart(2, '0'));
 let option = {
 tooltip: {
 trigger: 'axis',
 axisPointer: { type: 'shadow' }
 },
 legend: {
 data: this.years,
 right: "center",
 top: 0
 },
 grid: {
 left: '3%',
 right: '4%',
 bottom: '10%',
 top: '15%',
 containLabel: true
 },
 xAxis: [{
 type: 'category',
 data: allMonths
 }],
 yAxis: [{
 type: 'value',
 name: '数量'
 }],
 series: this.years.map((year) => ({
 name: year,
 type: 'line',
 data: this.claimYearData[year] || [],
 itemStyle: {
 normal: {
 label: {
 show: true,
 position: 'top',
 fontSize: 10
 }
 }
 }
 }))
 };
 this.chartInstances.claim.setOption(option, true);
 let _this = this;
 this.chartInstances.claim.off('click');
 this.chartInstances.claim.on('click', function (params) {
 const month = params.name;
 const year = params.seriesName;
 const yearMonth = `${year}-${month}`;
 const firstDay = `${year}-${month}-01`;
 let lastDayDate = new Date(parseInt(year), parseInt(month), 0);
 const lastDay = lastDayDate.getFullYear() + '-' + 
 String(lastDayDate.getMonth() + 1).padStart(2, '0') + '-' + 
 String(lastDayDate.getDate()).padStart(2, '0');
 _this.$emit('toPage', 'v4a964e28354dd5945af2da28ad', {
 material_id: _this.materialIds.join(','),
 back_month_btime: firstDay,
 back_month_etime: lastDay
 });
 });
 },
 renderDefectChart(nameList, numList) {
 if (!this.$refs.defectChart) return;
 if (!this.chartInstances.defect) {
 this.chartInstances.defect = this.$echarts.init(this.$refs.defectChart);
 }
 let total = numList.reduce((a, b) => a + b, 0);
 let percentList = numList.map(num => (num / total * 100).toFixed(1));
 let cumulative = [];
 let cumulativePercent = 0;
 numList.forEach(num => {
 cumulativePercent += (num / total * 100);
 cumulative.push(cumulativePercent.toFixed(1));
 });
 let option = {
 tooltip: {
 trigger: 'axis',
 axisPointer: { type: 'cross' }
 },
 grid: {
 left: '3%',
 right: '4%',
 bottom: '20%',
 top: '15%',
 containLabel: true
 },
 legend: {
 data: ['数量', '占比', '累计占比'],
 top: 0
 },
 xAxis: {
 type: 'category',
 data: nameList,
 axisLabel: {
 interval: 0,
 rotate: 60,
 fontSize: 9
 }
 },
 yAxis: [
 {
 type: 'value',
 name: '数量',
 position: 'left'
 },
 {
 type: 'value',
 name: '百分比',
 position: 'right',
 axisLabel: {
 formatter: '{value}%'
 },
 max: 100
 }
 ],
 series: [
 {
 name: '数量',
 type: 'bar',
 data: numList,
 itemStyle: { color: '#409EFF' },
 label: {
 show: true,
 position: 'top',
 fontSize: 9,
 formatter: function(params) {
 if (params.value < total * 0.01) return '';
 return params.value;
 }
 }
 },
 {
 name: '累计占比',
 type: 'line',
 data: cumulative,
 yAxisIndex: 1,
 itemStyle: { color: '#67C23A' },
 label: {
 show: true,
 fontSize: 9,
 formatter: function(params) {
 const index = params.dataIndex;
 if (index % 3 !== 0 && index !== cumulative.length - 1) return '';
 return params.value + '%';
 }
 }
 }
 ]
 };
 this.chartInstances.defect.setOption(option);
 let _this = this;
 this.chartInstances.defect.off('click');
 this.chartInstances.defect.on('click', function (params) {
 let defectId = _this.defectIds[params.dataIndex];
 let startDate = `${_this.startYear}-01`;
 const lastDay = new Date(parseInt(_this.endYear.split('-')[0]), parseInt(_this.endYear.split('-')[1]), 0).getDate();
 let endDate = `${_this.endYear}-${lastDay}`;
 let pageParams = {
 material_id: _this.materialIds.join(','),
 btime: startDate,
 etime: endDate
 };
 if (defectId && defectId !== 0) {
 pageParams.defect_id = defectId;
 }
 _this.$emit('toPage', 'v4a964e28354dd5945af2da28ad', pageParams);
 });
 },
 renderMileageChart(results) {
 if (!this.$refs.mileageChart) return;
 if (!this.chartInstances.mileage) {
 this.chartInstances.mileage = this.$echarts.init(this.$refs.mileageChart);
 }
 const nameList = results.map(item => item.range);
 const numList = results.map(item => item.count);
 let option = {
 tooltip: {
 trigger: 'axis',
 axisPointer: { type: 'shadow' }
 },
 grid: {
 left: '3%',
 right: '4%',
 bottom: '15%',
 containLabel: true
 },
 xAxis: {
 type: 'category',
 data: nameList,
 name: '里程区间(km)',
 axisLabel: {
 interval: 0,
 rotate: 45,
 fontSize: 10
 }
 },
 yAxis: {
 type: 'value',
 name: '数量'
 },
 series: [{
 data: numList,
 type: 'bar',
 itemStyle: {
 color: '#5470c6',
 normal: {
 label: {
 show: true,
 position: 'top',
 fontSize: 10
 }
 }
 }
 }]
 };
 this.chartInstances.mileage.setOption(option);
 let _this = this;
 this.chartInstances.mileage.off('click');
 this.chartInstances.mileage.on('click', function (params) {
 let range = params.name;
 let [min, max] = range.split('-').map(Number);
 let startDate = `${_this.startYear}-01`;
 const lastDay = new Date(parseInt(_this.endYear.split('-')[0]), parseInt(_this.endYear.split('-')[1]), 0).getDate();
 let endDate = `${_this.endYear}-${lastDay}`;
 _this.$emit('toPage', 'v4a964e28354dd5945af2da28ad', {
 material_id: _this.materialIds.join(','),
 drive_distance_min: min,
 drive_distance_max: max,
 btime: startDate,
 etime: endDate
 });
 });
 },
 renderStoreChart(nameList, numList) {
 if (!this.$refs.storeChart) return;
 if (!this.chartInstances.store) {
 this.chartInstances.store = this.$echarts.init(this.$refs.storeChart);
 }
 const total = numList.reduce((a, b) => a + b, 0);
 const percentList = numList.map(num => (num / total * 100).toFixed(1));
 const that = this;
 let option = {
 tooltip: {
 trigger: 'item',
 formatter: function(params) {
 return params.name + '<br/>' +
 '数量: ' + params.value + '<br/>' +
 '占比: ' + params.percent.toFixed(1) + '%';
 }
 },
 legend: {
 type: 'scroll',
 orient: 'vertical',
 right: '2%',
 top: 'middle',
 formatter: function(name) {
 const index = nameList.indexOf(name);
 if (index === -1) return name;
 const value = numList[index];
 const percent = percentList[index];
 return name + ' ' + value + ' (' + percent + '%)';
 }
 },
 series: [
 {
 name: '数量分布',
 type: 'pie',
 radius: ['40%', '70%'],
 center: ['35%', '50%'],
 avoidLabelOverlap: true,
 itemStyle: {
 borderRadius: 10,
 borderColor: '#fff',
 borderWidth: 2
 },
 label: {
 show: true,
 position: 'outside',
 formatter: '{b}: {c} ({d}%)',
 fontSize: 9
 },
 labelLine: {
 show: true,
 length: 5,
 length2: 5
 },
 data: nameList.map((name, index) => ({
 name: name,
 value: numList[index]
 })).sort((a, b) => b.value - a.value)
 },
 {
 name: '累计占比',
 type: 'pie',
 radius: ['0%', '30%'],
 center: ['35%', '50%'],
 label: {
 position: 'center',
 formatter: '累计\n100%',
 fontSize: 16,
 fontWeight: 'bold'
 },
 data: [{
 value: 100,
 name: '累计占比',
 itemStyle: { color: '#67C23A' }
 }]
 }
 ]
 };
 this.chartInstances.store.setOption(option);
 },
 renderUserDefectChart(nameList, numList) {
 if (!this.$refs.userDefectChart) return;
 if (!this.chartInstances.userDefect) {
 this.chartInstances.userDefect = this.$echarts.init(this.$refs.userDefectChart);
 }
 let total = numList.reduce((a, b) => a + b, 0);
 let percentList = numList.map(num => (num / total * 100).toFixed(1));
 let cumulative = [];
 let cumulativePercent = 0;
 numList.forEach(num => {
 cumulativePercent += (num / total * 100);
 cumulative.push(cumulativePercent.toFixed(1));
 });
 let option = {
 tooltip: {
 trigger: 'axis',
 axisPointer: { type: 'cross' }
 },
 grid: {
 left: '3%',
 right: '4%',
 bottom: '20%',
 top: '15%',
 containLabel: true
 },
 legend: {
 data: ['数量', '占比', '累计占比'],
 top: 0
 },
 xAxis: {
 type: 'category',
 data: nameList,
 axisLabel: {
 interval: 0,
 rotate: 60,
 fontSize: 9
 }
 },
 yAxis: [
 {
 type: 'value',
 name: '数量',
 position: 'left'
 },
 {
 type: 'value',
 name: '百分比',
 position: 'right',
 axisLabel: {
 formatter: '{value}%'
 },
 max: 100
 }
 ],
 series: [
 {
 name: '数量',
 type: 'bar',
 data: numList,
 itemStyle: { color: '#409EFF' },
 label: {
 show: true,
 position: 'top',
 fontSize: 9,
 formatter: function(params) {
 if (params.value < total * 0.01) return '';
 return params.value;
 }
 }
 },
 {
 name: '累计占比',
 type: 'line',
 data: cumulative,
 yAxisIndex: 1,
 itemStyle: { color: '#67C23A' },
 label: {
 show: true,
 fontSize: 9,
 formatter: function(params) {
 const index = params.dataIndex;
 if (index % 3 !== 0 && index !== cumulative.length - 1) return '';
 return params.value + '%';
 }
 }
 }
 ]
 };
 this.chartInstances.userDefect.setOption(option);
 let _this = this;
 this.chartInstances.userDefect.off('click');
 this.chartInstances.userDefect.on('click', function (params) {
 let defectMode = params.name;
 let startDate = `${_this.startYear}-01`;
 const lastDay = new Date(parseInt(_this.endYear.split('-')[0]), parseInt(_this.endYear.split('-')[1]), 0).getDate();
 let endDate = `${_this.endYear}-${lastDay}`;
 _this.$emit('toPage', 'v4a964e28354dd5945af2da28ad', {
 material_id: _this.materialIds.join(','),
 consumer_describe_extract: defectMode,
 btime: startDate,
 etime: endDate
 });
 });
 },
 updateClaimTableData() {
 this.claimHeaders = [
 { text: '月份', value: 'month', width: '100px' },
 ...this.years.map(year => ({
 text: year,
 value: year,
 align: 'center'
 }))
 ];
 this.claimTableData = Array.from({length: 12}, (_, i) => {
 const month = String(i + 1).padStart(2, '0');
 const item = { month };
 this.years.forEach(year => {
 item[year] = this.claimYearData[year] ? this.claimYearData[year][i] : 0;
 });
 return item;
 });
 const totalRow = { month: '合计' };
 this.years.forEach(year => {
 totalRow[year] = this.claimTableData.reduce((sum, item) => sum + item[year], 0);
 });
 this.claimTableData.push(totalRow);
 },
 exportExcel(type) {
 try {
 let csvContent = '\uFEFF', filename = '';
 if (type === 'market') {
 csvContent += "年份,月份,故障数,发货数,故障率\n";
 this.years.forEach(year => {
 this.getMonthsForYear(year).forEach(month => {
 const ym = `${year}-${month}`, idx = this.marketRateData.nameList.indexOf(ym);
 csvContent += `${year},${month},${idx >= 0 ? this.marketRateData.faultData[idx] : ''},${idx >= 0 ? this.marketRateData.sendData[idx] : ''},${idx >= 0 ? this.marketRateData.rateData[idx] : ''}\n`;
 });
 });
 csvContent += `\n,总故障率,${this.marketRateData.avgRate}\n`;
 filename = '市场不良率.csv';
 } else if (type === 'batch') {
 csvContent += "生产日期,故障数量,发货数量,故障率(%)\n";
 this.batchTableData.forEach(item => csvContent += `${item.name},${item.num},${item.sendNum},${item.rate}\n`);
 filename = '故障件生产批次分布.csv';
 } else if (type === 'claim') {
 csvContent += "售后索赔日期数量分布\n\n月份," + this.years.join(',') + '\n';
 this.claimTableData.forEach(item => {
 const row = [item.month === '合计' ? '合计' : item.month + '月'];
 this.years.forEach(year => row.push(item[year]));
 csvContent += row.join(',') + '\n';
 });
 filename = '售后索赔日期数量分布.csv';
 } else if (type === 'mileage') {
 csvContent += '里程区间(km),数量\n';
 this.mileageTableData.forEach(item => csvContent += `${item.range},${item.count}\n`);
 filename = '故障里程分布.csv';
 } else {
 csvContent += '缺陷模式,数量,占比(%),累计占比(%)\n';
 const data = type === 'defect' ? this.defectTableData : type === 'store' ? this.storeTableData : this.userDefectTableData;
 data.forEach(item => csvContent += `${item.name.includes(',') ? '"'+item.name+'"' : item.name},${item.value},${item.percentage},${item.cumulative}\n`);
 filename = (type === 'defect' ? '售后分析缺陷模式分布' : type === 'store' ? '售后经销商4S店分布' : '售后用户描述缺陷模式分布') + '.csv';
 }
 const blob = new Blob([csvContent], {type: 'text/csv;charset=utf-8;'});
 const link = document.createElement('a');
 link.href = URL.createObjectURL(blob);
 link.download = filename;
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);
 URL.revokeObjectURL(link.href);
 } catch (error) {}
 },
 exportMarketRate() { this.exportExcel('market'); }, exportBatchExcel() { this.exportExcel('batch'); }, exportClaimExcel() { this.exportExcel('claim'); }, exportMileageExcel() { this.exportExcel('mileage'); }, exportDefectExcel() { this.exportExcel('defect'); }, exportStoreExcel() { this.exportExcel('store'); }, exportUserDefectExcel() { this.exportExcel('user'); },
 exportChartImage(chartName, title) {
 if (!this.chartInstances[chartName]) return;
 const imageUrl = this.chartInstances[chartName].getDataURL({type: 'png', pixelRatio: 2, backgroundColor: '#fff'});
 const link = document.createElement('a');
 link.download = title + '.png';
 link.href = imageUrl;
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);
 },
 exportBatchChart() { this.exportChartImage('batch', '故障件生产批次分布'); }, exportClaimChart() { this.exportChartImage('claim', '售后索赔日期数量分布'); }, exportDefectChart() { this.exportChartImage('defect', '售后分析缺陷模式分布'); }, exportMileageChart() { this.exportChartImage('mileage', '故障里程分布'); }, exportStoreChart() { this.exportChartImage('store', '售后经销商4S店分布'); }, exportUserDefectChart() { this.exportChartImage('userDefect', '售后用户描述缺陷模式分布'); },
 getYearSpan(year) {
 if (!this.marketRateData.nameList || !Array.isArray(this.marketRateData.nameList)) {
 return 0;
 }
 return this.marketRateData.nameList.filter(name => name && name.startsWith(year)).length;
 },
 getMonthsForYear(year) {
 if (!this.marketRateData.nameList || !Array.isArray(this.marketRateData.nameList)) {
 return [];
 }
 return this.marketRateData.nameList
 .filter(name => name && name.startsWith(year))
 .map(name => name.split('-')[1]);
 },
 getMarketDataByYearMonth(year, month, type) {
 if (!this.marketRateData.nameList || !Array.isArray(this.marketRateData.nameList)) {
 return '';
 }
 const ym = `${year}-${month}`;
 const index = this.marketRateData.nameList.indexOf(ym);
 if (index === -1) return '';
 if (type === 'fault') return this.marketRateData.faultData[index] || '0';
 if (type === 'send') return this.marketRateData.sendData[index] || '';
 if (type === 'rate') return this.marketRateData.rateData[index] || '';
 return '';
 },
 handleResize() {
 Object.values(this.chartInstances).forEach(chart => {
 if (chart && chart.resize) {
 chart.resize();
 }
 });
 }
 },
 watch: {
 selectedMaterial() {
 if (this.selectedMaterial) {
 this.selectedReferred = [];
 }
 },
 selectedReferred() {
 if (this.selectedReferred && this.selectedReferred.length > 0) {
 this.selectedMaterial = '';
 }
 }
 },
 created() {
 this.initYearOptions();
 this.loadMaterialData();
 this.loadReferredData();
 },
 mounted() {
 window.addEventListener('resize', this.handleResize);
 },
 beforeDestroy() {
 window.removeEventListener('resize', this.handleResize);
 Object.values(this.chartInstances).forEach(chart => {
 if (chart && chart.dispose) {
 chart.dispose();
 }
 });
 }
};
</script>
<style scoped>
.chart-card{height:100%}
.chart-title{font-size:14px;font-weight:bold;padding:8px 16px;background-color:#f5f5f5}
.excel-style-table{border:2px solid black;border-collapse:separate;border-spacing:0}
.excel-style-table th,.excel-style-table td{border:1px solid black;padding:4px 8px;text-align:center}
.excel-style-table th{background-color:#f5f5f5;font-weight:bold}
.excel-style-table .hdr{vertical-align:middle;border-bottom:thin solid black;text-align:center;border-right:thin solid black}
.v-btn{text-transform:none}
</style>
